package com.pku.smart.trade.channel.wechatpay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.result.WxPayBillInfo;
import com.github.binarywang.wxpay.bean.result.WxPayBillResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.pku.smart.account.entity.AccountPayChannel;
import com.pku.smart.account.service.IAccountPayChannelService;
import com.pku.smart.config.WechatPayConfig;
import com.pku.smart.constant.PayConstant;
import com.pku.smart.log.MyLog;
import com.pku.smart.trade.channel.PayChannelService;
import com.pku.smart.trade.enums.TradePayStatusEnum;
import com.pku.smart.trade.enums.TradeStatusEnum;
import com.pku.smart.trade.vopackage.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class WechatPayChannelService implements PayChannelService {

    private final MyLog _log = MyLog.getLog(WechatPayChannelService.class);

    private WxPayService wxPayService;

    @Autowired
    WechatPayConfig payConfig;

    @Autowired
    IAccountPayChannelService accountPayChannelService;

    /**
     * 初始化配置
     *
     * @param mchId
     * @param channelId
     */
    @Override
    public void init(String mchId, String channelId) {
        _log.info("============开始构建微信服务============");
        AccountPayChannel payChannel = accountPayChannelService.getPayChannel(mchId, channelId);
        String configParam = payChannel.getParam();
        JSONObject paramObj = JSON.parseObject(configParam);
        String tradeType = "";//目前仅支持扫码支付 JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
        if (channelId.equals(PayConstant.PAY_CHANNEL_WX_MICROPAY)) {
            tradeType = "MICROPAY";//MICROPAY 刷卡支付
        } else if (channelId.equals(PayConstant.PAY_CHANNEL_WX_NATIVE)) {
            tradeType = "NATIVE";
        } else if (channelId.equals(PayConstant.PAY_CHANNEL_WX_JSAPI)) {
            tradeType = "JSAPI";
        }
        _log.info("微信支付类型：{}", tradeType);
        String certRootPath = payConfig.getCertRootPath();
        _log.info("证书目录：{}", certRootPath);
        String notifyUrl = payConfig.getNotifyUrl();
        _log.info("域名地址：{}", notifyUrl);
        notifyUrl = notifyUrl + "/" + mchId + "/pay.action";
        _log.info("构建支付回调通知地址：{}", notifyUrl);

        WxPayConfig wxPayConfig = new WxPayConfig();
        wxPayConfig.setMchId(paramObj.getString("mchId"));
        wxPayConfig.setAppId(paramObj.getString("appId"));
        wxPayConfig.setKeyPath(certRootPath);
        wxPayConfig.setMchKey(paramObj.getString("key"));
        wxPayConfig.setSignType(payConfig.getSignType());
        wxPayConfig.setNotifyUrl(notifyUrl);
        wxPayConfig.setTradeType(tradeType);
        wxPayConfig.setUseSandboxEnv(payConfig.getIsSandbox());
        if (wxPayConfig.isUseSandboxEnv()) {
            WxPayService wxPayService = new WxPayServiceImpl();
            wxPayConfig.setMchKey("正式秘钥");
            wxPayService.setConfig(wxPayConfig);
            try {
                String key = wxPayService.getSandboxSignKey();
                _log.info("沙箱密钥：{}", key);
            } catch (WxPayException e) {
                e.printStackTrace();
            }
            wxPayConfig.setMchKey("f7d5915d6736cbe1bd7e4113cce48b82");
            _log.warn("沙箱测试");
        }
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(wxPayConfig);
        this.wxPayService = wxPayService;
        _log.info("============构建微信服务结束============");
    }

    /**
     * 条码支付接口
     * 关键字段 tradeStatus-支付状态 channelOrderNo-渠道支付单号
     * 支付状态：1、成功 2、支付中 3、系统错误
     * @param requestVo
     * @return
     */
    @Override
    public JSONObject pay(VoReqPayCode requestVo) {
        JSONObject resultMap = new JSONObject();

        //公共返回数据
        resultMap.put("mchId", requestVo.getMchId());
        resultMap.put("channelId", requestVo.getChannelId());
        resultMap.put("mchOrderNo", requestVo.getMchOrderNo());
        resultMap.put("totalAmount", requestVo.getTotalAmount());

        String outTradeNo = requestVo.getMchOrderNo();
        String body = requestVo.getSubject();
        Integer totalFee = requestVo.getTotalAmount().intValue();//元
        String authCode = requestVo.getAuthCode();
        String detail = requestVo.getBody();
        String attach = requestVo.getMchId();
        String spbillCreateIp = requestVo.getClientIp();

        JSONObject tradeMap = null;
        try {
            tradeMap = WechatPayUtils.payMicropay(wxPayService, outTradeNo, body, detail, totalFee, spbillCreateIp, authCode, attach);

            //调用成功
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_SUCCESS.getCode());
            resultMap.put("errCode", tradeMap.getString("errCode"));
            resultMap.put("errMsg", tradeMap.getString("errCodeDes"));
            if ("SUCCESS".equals(tradeMap.getString("returnCode")) && "SUCCESS".equals(tradeMap.getString("resultCode"))){
                _log.info("支付成功");
                resultMap.put("channelOrderNo", tradeMap.getString("transactionId"));
                resultMap.put("tradeStatus", tradeMap.getString("resultCode"));
            } else {
                _log.info("支付未成功");
                resultMap.put("tradeStatus", tradeMap.getString("resultCode"));
            }
            resultMap.put("openId", tradeMap.getString("openid"));
        } catch (WxPayException e) {
            e.printStackTrace();
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
            //可能情况 1、用户支付成功，微信支付返回超时 2、用户支付失败，微信支付返回超时 3、微信支付返回超时，且查单失败
            if ("Read timed out".equalsIgnoreCase(e.getCustomErrorMsg())) {
                _log.info("超时，转到查询");
                resultMap.put("errCode", TradeStatusEnum.TRADE_STATUS_TIMEOUT.getCode());
                resultMap.put("errMsg", "交易超时,请重试!");
                resultMap.put("tradeStatus", e.getCustomErrorMsg());
                return resultMap;
            }
            String errCode = StringUtils.isBlank(e.getErrCode()) ? e.getReturnCode() : e.getErrCode();
            resultMap.put("errCode", errCode);
            resultMap.put("errMsg", StringUtils.isBlank(e.getErrCodeDes()) ? e.getReturnMsg() : e.getErrCodeDes());
            resultMap.put("tradeStatus", errCode);
            return resultMap;
        } finally {
            _log.info("渠道执行返回：" + JSON.toJSONString(resultMap));
        }

        return resultMap;
    }

    /**
     * 三码支付
     *
     * @param requestVo
     * @return
     */
    @Override
    public JSONObject precreate(VoReqPayScan requestVo) {
        JSONObject resultMap = new JSONObject();

        //公共返回数据
        resultMap.put("mchId", requestVo.getMchId());
        resultMap.put("channelId", requestVo.getChannelId());
        resultMap.put("mchOrderNo", requestVo.getMchOrderNo());
        resultMap.put("totalAmount", requestVo.getTotalAmount());

        String deviceInfo = requestVo.getDevice();
        String body = requestVo.getSubject();
        String detail = requestVo.getBody();
        String attach = requestVo.getMchId();
        String outTradeNo = requestVo.getMchOrderNo();
        Integer totalFee = requestVo.getTotalAmount().intValue();
        String spBillCreateIP = requestVo.getClientIp();
        String notifyUrl = requestVo.getNotifyUrl();
        String tradeType = "";
        String productId = "";
        if (PayConstant.PAY_CHANNEL_WX_NATIVE.equalsIgnoreCase(requestVo.getChannelId())) {
            tradeType = "NATIVE";
            if (StringUtils.isBlank(productId)){
                throw new RuntimeException("微信NATIVE支付需要传extra参数");
            }
            productId = JSON.parseObject(requestVo.getExtra()).getString("productId");
        }
        String openId = null;

        JSONObject tradeMap = null;
        try {
            tradeMap = WechatPayUtils.payUnifiedOrder(wxPayService, deviceInfo, body, detail, attach, outTradeNo, totalFee, spBillCreateIP, notifyUrl, tradeType, productId, openId);
            //调用成功
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_SUCCESS.getCode());
            resultMap.put("errCode", tradeMap.getString("errCode"));
            resultMap.put("errMsg", tradeMap.getString("errCodeDes"));

            resultMap.put("codeUrl", tradeMap.getString("codeURL"));
        } catch (WxPayException e) {
            e.printStackTrace();
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
            String errCode = StringUtils.isBlank(e.getErrCode()) ? e.getReturnCode() : e.getErrCode();
            String errMsg = StringUtils.isBlank(e.getErrCodeDes()) ? e.getReturnMsg() : e.getErrCodeDes();
            resultMap.put("errCode", StringUtils.isBlank(errCode) ? PayConstant.TRADE_STATUS_SYSTEM_ERROR_CODE : errCode);
            resultMap.put("errMsg", StringUtils.isBlank(errMsg) ? e.getCustomErrorMsg() : errMsg);
            return resultMap;
        } finally {
            _log.info("渠道执行返回：" + JSON.toJSONString(resultMap));
        }

        return resultMap;
    }

    /**
     * 查询条码支付结果
     *
     * @param requestVo
     * @return
     */
    @Override
    public JSONObject query(VoReqQueryPay requestVo) {
        JSONObject resultMap = new JSONObject();

        //公共返回数据
        resultMap.put("mchId", requestVo.getMchId());
        resultMap.put("mchOrderNo", requestVo.getMchOrderNo());
        resultMap.put("channelOrderNo", requestVo.getChannelOrderNo());

        String transactionId = requestVo.getChannelOrderNo();
        String outTradeNo = requestVo.getMchOrderNo();

        JSONObject tradeMap = null;
        try {
            tradeMap = WechatPayUtils.payOrderQuery(wxPayService, transactionId, outTradeNo);

            //调用成功
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_SUCCESS.getCode());
            resultMap.put("errCode", tradeMap.getString("errCode"));
            resultMap.put("errMsg", tradeMap.getString("errCodeDes"));
            resultMap.put("channelOrderNo", tradeMap.getString("transactionId"));

            if ("SUCCESS".equalsIgnoreCase(tradeMap.getString("tradeState"))) {
                resultMap.put("tradeStatus", TradePayStatusEnum.PAY_STATUS_SUCCESS.name());
                resultMap.put("errCode", tradeMap.getString("tradeState"));
                resultMap.put("errMsg", tradeMap.getString("tradeStateDesc"));
            } else {
                resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
                resultMap.put("errCode", tradeMap.getString("tradeState"));
                resultMap.put("errMsg", "状态需要再次确认");
                resultMap.put("tradeStatus", TradePayStatusEnum.PAY_STATUS_FAILED.name());
            }
        } catch (WxPayException e) {
            e.printStackTrace();
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
            String errCode = StringUtils.isBlank(e.getErrCode()) ? e.getReturnCode() : e.getErrCode();
            String errMsg = StringUtils.isBlank(e.getErrCodeDes()) ? e.getReturnMsg() : e.getErrCodeDes();
            resultMap.put("errCode", StringUtils.isBlank(errCode) ? PayConstant.TRADE_STATUS_SYSTEM_ERROR_CODE : errCode);
            resultMap.put("errMsg", StringUtils.isBlank(errMsg) ? e.getCustomErrorMsg() : errMsg);

            return resultMap;
        } finally {
            _log.info("渠道执行返回：" + JSON.toJSONString(resultMap));
        }

        return resultMap;
    }

    /**
     * 退款
     *
     * @param requestVo
     * @return
     */
    @Override
    public JSONObject refund(VoReqRefund requestVo) {
        JSONObject resultMap = new JSONObject();

        //公共返回数据
        resultMap.put("mchId", requestVo.getMchId());
        resultMap.put("mchOrderNo", requestVo.getMchOrderNo());
        resultMap.put("channelOrderNo", requestVo.getChannelOrderNo());
        resultMap.put("mchRefundNo", requestVo.getMchRefundNo());
        resultMap.put("refundAmount", requestVo.getRefundAmount());

        String transactionId = requestVo.getChannelOrderNo();
        String outTradeNo = requestVo.getMchOrderNo();
        String outRefundNo = requestVo.getMchRefundNo();
        Integer totalFee = requestVo.getTotalAmount().intValue();
        Integer refundFee = requestVo.getRefundAmount().intValue();
        String refundDesc = requestVo.getRefundReason();
        String notifyUrl = payConfig.getNotifyUrl() + "/" + requestVo.getMchId() + "/refund.action";
        _log.info("构建支付回调通知地址：{}", notifyUrl);

        JSONObject tradeMap = null;
        try {
            tradeMap = WechatPayUtils.payRefund(wxPayService, transactionId, outTradeNo, outRefundNo, totalFee, refundFee, refundDesc, notifyUrl);

            //调用成功
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_SUCCESS.getCode());
            resultMap.put("errCode", tradeMap.getString("errCode"));
            resultMap.put("errMsg", tradeMap.getString("errCodeDes"));
            resultMap.put("channelRefundOrderNo", "");
        } catch (WxPayException e) {
            e.printStackTrace();
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
            String errCode = StringUtils.isBlank(e.getErrCode()) ? e.getReturnCode() : e.getErrCode();
            String errMsg = StringUtils.isBlank(e.getErrCodeDes()) ? e.getReturnMsg() : e.getErrCodeDes();
            resultMap.put("errCode", StringUtils.isBlank(errCode) ? PayConstant.TRADE_STATUS_SYSTEM_ERROR_CODE : errCode);
            resultMap.put("errMsg", StringUtils.isBlank(errMsg) ? e.getCustomErrorMsg() : errMsg);
            return resultMap;
        } finally {
            _log.info("渠道执行返回：" + JSON.toJSONString(resultMap));
        }

        return resultMap;
    }

    /**
     * 退款查询
     *
     * @param requestVo
     * @return
     */
    @Override
    public JSONObject queryRefund(VoReqQueryRefund requestVo) {
        JSONObject resultMap = new JSONObject();

        //公共返回数据
        resultMap.put("mchId", requestVo.getMchId());
        resultMap.put("mchOrderNo", requestVo.getMchRefundNo());
        resultMap.put("channelOrderNo", requestVo.getChannelOrderNo());

        String transactionId = requestVo.getChannelOrderNo();
        String outTradeNo = requestVo.getMchOrderNo();
        String outRefundNo = requestVo.getMchRefundNo();
        String refundId = null;

        JSONObject tradeMap = null;
        try {
            tradeMap = WechatPayUtils.refundOrderQuery(wxPayService, transactionId, outTradeNo, outRefundNo, refundId);

            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_SUCCESS.getCode());
            resultMap.put("errCode", tradeMap.getString("errCode"));
            resultMap.put("errMsg", tradeMap.getString("errCodeDes"));
            resultMap.put("channelRefundOrderNo", "");
        } catch (WxPayException e) {
            e.printStackTrace();
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
            String errCode = StringUtils.isBlank(e.getErrCode()) ? e.getReturnCode() : e.getErrCode();
            String errMsg = StringUtils.isBlank(e.getErrCodeDes()) ? e.getReturnMsg() : e.getErrCodeDes();
            resultMap.put("errCode", StringUtils.isBlank(errCode) ? PayConstant.TRADE_STATUS_SYSTEM_ERROR_CODE : errCode);
            resultMap.put("errMsg", StringUtils.isBlank(errMsg) ? e.getCustomErrorMsg() : errMsg);
            return resultMap;
        } finally {
            _log.info("渠道执行返回：" + JSON.toJSONString(resultMap));
        }

        return resultMap;
    }

    /**
     * 撤销
     *
     * @param requestVo
     * @return
     */
    @Override
    public JSONObject reverse(VoReqReverse requestVo) {
        JSONObject resultMap = new JSONObject();

        //公共返回数据
        resultMap.put("mchId", requestVo.getMchId());
        resultMap.put("mchOrderNo", requestVo.getMchOrderNo());

        String outTradeNo = requestVo.getMchOrderNo();
        String transactionId = requestVo.getChannelOrderNo();

        JSONObject tradeMap = null;
        try {
            tradeMap = WechatPayUtils.payOrderReverse(wxPayService, transactionId, outTradeNo);

            //调用成功
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_SUCCESS.getCode());
            resultMap.put("errCode", tradeMap.getString("errCode"));
            resultMap.put("errMsg", tradeMap.getString("errCodeDes"));

            if ("Y".equalsIgnoreCase(tradeMap.getString("isRecall"))) {
                resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
                resultMap.put("errMsg", tradeMap.getString("errCodeDes") + "(需要继续调用撤销)");
            }
        } catch (WxPayException e) {
            e.printStackTrace();
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
            String errCode = StringUtils.isBlank(e.getErrCode()) ? e.getReturnCode() : e.getErrCode();
            String errMsg = StringUtils.isBlank(e.getErrCodeDes()) ? e.getReturnMsg() : e.getErrCodeDes();
            resultMap.put("errCode", StringUtils.isBlank(errCode) ? PayConstant.TRADE_STATUS_SYSTEM_ERROR_CODE : errCode);
            resultMap.put("errMsg", StringUtils.isBlank(errMsg) ? e.getCustomErrorMsg() : errMsg);
            return resultMap;
        } finally {
            _log.info("渠道执行返回：" + JSON.toJSONString(resultMap));
        }

        return resultMap;
    }

    /**
     * 手机网站支付
     * 返回预支付交易会话标识
     * @param requestVo
     * @return
     */
    @Override
    public JSONObject wappay(VoReqPayWap requestVo) {
        JSONObject resultMap = new JSONObject();

        //公共返回数据
        resultMap.put("mchId", requestVo.getMchId());
        resultMap.put("channelId", requestVo.getChannelId());
        resultMap.put("mchOrderNo", requestVo.getMchOrderNo());
        resultMap.put("totalAmount", requestVo.getTotalAmount());

        String deviceInfo = requestVo.getDevice();
        String body = requestVo.getSubject();
        String detail = requestVo.getBody();
        String attach = requestVo.getMchId();
        String outTradeNo = requestVo.getMchOrderNo();
        Integer totalFee = requestVo.getTotalAmount().intValue();
        String spBillCreateIP = requestVo.getClientIp();
        String notifyUrl = requestVo.getNotifyUrl();
        String tradeType = "";
        String productId = "";
        String openId = requestVo.getOpenId();

        if (StringUtils.isBlank(openId)){
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
            resultMap.put("errCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
            resultMap.put("errMsg", "请传入有效的openid");
        }

        JSONObject tradeMap = null;
        try {
            tradeMap = WechatPayUtils.payUnifiedOrder(wxPayService, deviceInfo, body, detail, attach, outTradeNo, totalFee, spBillCreateIP, notifyUrl, tradeType, productId, openId);
            //调用成功
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_SUCCESS.getCode());
            resultMap.put("errCode", tradeMap.getString("errCode"));
            resultMap.put("errMsg", tradeMap.getString("errCodeDes"));

            resultMap.put("prepayId", tradeMap.getString("prepayId"));
        } catch (WxPayException e) {
            e.printStackTrace();
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
            String errCode = StringUtils.isBlank(e.getErrCode()) ? e.getReturnCode() : e.getErrCode();
            String errMsg = StringUtils.isBlank(e.getErrCodeDes()) ? e.getReturnMsg() : e.getErrCodeDes();
            resultMap.put("errCode", StringUtils.isBlank(errCode) ? PayConstant.TRADE_STATUS_SYSTEM_ERROR_CODE : errCode);
            resultMap.put("errMsg", StringUtils.isBlank(errMsg) ? e.getCustomErrorMsg() : errMsg);
            return resultMap;
        } finally {
            _log.info("渠道执行返回：" + JSON.toJSONString(resultMap));
        }

        return resultMap;
    }

    /**
     * 对账单下载
     * @param requestVo
     * @return
     */
    @Override
    public JSONObject downbill(VoReqDownBill requestVo) {
        JSONObject resultMap = new JSONObject();

        //公共返回数据
        resultMap.put("mchId", requestVo.getMchId());
        resultMap.put("channelName", requestVo.getChannelName());
        resultMap.put("billDate", requestVo.getBillDate());

        _log.info("参数bill_type:ALL返回当日所有订单信息,默认值SUCCESS返回当日成功支付的订单。REFUND，返回当日退款订单。");
        String billType = "ALL";//ALL，返回当日所有订单信息 REFUND，返回当日退款订单 SUCCESS，返回当日成功支付的
        String billDate = requestVo.getBillDate().replaceAll("-","");
        String tarType = "";
        String deviceInfo = "";

        JSONObject tradeMap = new JSONObject();
        try {
            WxPayBillResult wxPayBillResult = WechatPayUtils.billOrderDownload(wxPayService,billType,billDate,tarType,deviceInfo);
            //调用成功
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_SUCCESS.getCode());
            resultMap.put("errCode", PayConstant.TRADE_STATUS_SUCCESS);
            resultMap.put("errMsg", PayConstant.TRADE_STATUS_SUCCESS);
            List<WxPayBillInfo> list = wxPayBillResult.getBillInfoList();
            resultMap.put("billData",list);
        } catch (WxPayException e) {
            e.printStackTrace();
            resultMap.put("retCode", TradeStatusEnum.TRADE_STATUS_FAILED.getCode());
            String errCode = StringUtils.isBlank(e.getReturnCode()) ? e.getErrCode() : e.getReturnCode();
            String errMsg = StringUtils.isBlank(e.getReturnMsg()) ? e.getErrCodeDes() : e.getReturnMsg();
            resultMap.put("errCode", StringUtils.isBlank(errCode) ? PayConstant.TRADE_STATUS_SYSTEM_ERROR_CODE : errCode);
            resultMap.put("errMsg", StringUtils.isBlank(errMsg) ? e.getCustomErrorMsg() : errMsg);
            return resultMap;
        } finally {
            _log.info("渠道执行返回：" + JSON.toJSONString(resultMap));
        }
        return resultMap;
    }

    public WxPayService getWxPayService() {
        return wxPayService;
    }

    public void setWxPayService(WxPayService wxPayService) {
        this.wxPayService = wxPayService;
    }
}
